home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
doom
/
ldhe-src.0
/
ldhe-src
/
dehacked
/
source
/
files.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-31
|
49KB
|
1,754 lines
// DeHackEd version 2.3
// Written by Greg Lewis, gregl@umich.edu
// If you release any versions of this code, please include
// the author in the credits. Give credit where credit is due!
#include <sys/param.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "dehacked.h"
#include "files.h"
#include "linux_text.h"
// This is a fun function -- Basically, change the gender of all the data. ;-)
static int gender_table[NUMVERS] = {
LITTLE_ENDIAN, // DOOM1_2
LITTLE_ENDIAN, // DOOM1_6
LITTLE_ENDIAN, // DOOM2_0
LITTLE_ENDIAN, // DOOM1_9
LITTLE_ENDIAN, // LNX_X18
LITTLE_ENDIAN, // LNX_S18
BIG_ENDIAN, // SGI_X18
};
#define bytesex_row(data, fields, V) \
if ( BYTE_ORDER != gender_table[V] ) { \
for ( int sexi=0; sexi<fields; ++sexi ) \
data[sexi] = bytesex(data[sexi]); \
} \
#define bytesex_table(data, objs, fields, V) \
if ( BYTE_ORDER != gender_table[V] ) { \
for ( int sexi=0; sexi<objs; ++sexi ) { \
for ( int sexj=0; sexj<fields; ++sexj ) \
data[sexi][sexj] = bytesex(data[sexi][sexj]); \
} \
} \
// Try to find a given Doom file.
int Checkforfile(char *doomfile, char *arg1, char *doomext, char *type, FILE **fp)
{
EBool commandname = NO, found;
char filename[80];
// If there's nothing specified in the ini, use the command line.
if (strlen(doomfile) == 0 || strlen(arg1) != 0)
commandname = YES;
// This (attempts to) find the doom.exe file
if (commandname == YES)
{
strcpy(filename, arg1);
strcat(filename, "doom");
strcat(filename, doomext);
}
else
strcpy(filename, doomfile);
printf("Checking %s for doom%s... ", filename, doomext);
fflush(stdout);
if ((*fp = fopen(filename, type)) == NULL)
{
// Cheap kludge. Swap a 2 in on the end of the filename (change from
// "doom.exe" to "doom2.exe", if we're running command-line AND
// the name extension isn't "hack.exe", which it will be for the
// backup file.
if ((commandname == YES) && (strlen(doomext) < 7))
{
// Slip a "2" into the filename before the extension.
strcpy(strstr(filename, doomext), "2");
strcat(filename, doomext);
printf("not found!\r\nChecking %s for doom2%s...", filename, doomext);
if ((*fp = fopen(filename, "r+b")) == NULL)
found = NO;
else
found = YES;
}
else
found = NO;
}
else
found = YES;
if (found == YES)
{
printf("found!\r\n");
strcpy(doomfile, filename);
return 0;
}
else
{
printf("not found!\r\n");
return -1;
}
}
// Converts a patch file from 1.2 to 1.666 format.
void Convertpatch(FILE *patchp, char patchformat)
{
int i, j;
long buffer[22];
char forder[7] = {NORMALFRAME, MOVEFRAME, INJUREFRAME, CLOSEATTACKFRAME,
FARATTACKFRAME, DEATHFRAME, EXPLDEATHFRAME};
char sorder[5] = {ALERTSOUND, ATTACKSOUND, PAINSOUND, DEATHSOUND, ACTSOUND};
for (i=0; i<numobj[THING][DOOM1_2]-1; i++)
{
fread(buffer, size[THING][DOOM1_2], 1, patchp);
for (j=0; j<5; j++)
buffer[sorder[j]] = soundconvar[buffer[sorder[j]]];
for (j=0; j<7; j++)
buffer[forder[j]] = frameconvar[buffer[forder[j]]];
memcpy(thingdata[thingconvar[i]], buffer, size[THING][DOOM1_2]);
}
fread(maxammodata, size[AMMO][DOOM1_2], numobj[AMMO][DOOM1_2], patchp);
fread(perammodata, size[AMMO][DOOM1_2], numobj[AMMO][DOOM1_2], patchp);
for (i=0; i<numobj[WEAPON][DOOM1_2]; i++)
{
fread(buffer, size[WEAPON][DOOM1_2], 1, patchp);
for (j=0; j<5; j++)
buffer[j+1] = frameconvar[buffer[j+1]];
memcpy(weapondata[i], buffer, size[WEAPON][DOOM1_2]);
}
if (patchformat == 2)
{
for (i=0; i<numobj[FRAME][DOOM1_2]; i++)
{
fread(buffer, size[FRAME][DOOM1_2], 1, patchp);
buffer[SPRITENUM] = spriteconvar[buffer[SPRITENUM]];
buffer[NEXTFRAME] = frameconvar[buffer[NEXTFRAME]];
memcpy(framedata[frameconvar[i]], buffer, size[FRAME][DOOM1_2]);
}
}
}
// Creates a save patch file that contains only differences between
// the current doom exe and the backup one. Note: this function is
// long and unwieldy. Inelegant code sucks.
int CreateDiffSave(FILE *patchp)
{
long (*thingorig )[THING_FIELDS];
long (*frameorig )[FRAME_FIELDS];
long (*soundorig )[SOUND_FIELDS];
long (*weaponorig)[WEAPON_FIELDS];
long *spriteorig;
long *maxammoorig;
long *perammoorig;
char *textorigp;
EBool nameprinted;
int i, j;
// Initialize memory according to largest values needed, for loading
// original data from the unaltered exe file.
thingorig = new long[numobj[THING][version]][THING_FIELDS];
soundorig = new long[numobj[SOUND][version]][SOUND_FIELDS];
frameorig = new long[numobj[FRAME][version]][FRAME_FIELDS];
spriteorig = new long[numobj[SPRITE][version]];
maxammoorig= new long[numobj[AMMO][version]];
perammoorig= new long[numobj[AMMO][version]];
weaponorig = new long[numobj[WEAPON][version]][WEAPON_FIELDS];
textorigp = new char[size[TXT][version]];
// Check if any of the memory didn't come though, and return if so.
if (thingorig == NULL || soundorig == NULL || frameorig == NULL ||
spriteorig == NULL || maxammoorig == NULL || perammoorig == NULL ||
weaponorig == NULL || textorigp == NULL)
return -1;
// Move all of the data that's currently here (original data)
// to a safe place, so we can load in the other exe data.
memmove(thingorig, thingdata, numobj[THING][version]*THING_FIELDS*4);
memmove(soundorig, sounddata, numobj[SOUND][version]*SOUND_FIELDS*4);
memmove(frameorig, framedata, numobj[FRAME][version]*FRAME_FIELDS*4);
memmove(spriteorig, spritedata, numobj[SPRITE][version]*4);
memmove(maxammoorig, maxammodata, numobj[AMMO][version]*4);
memmove(perammoorig, perammodata, numobj[AMMO][version]*4);
memmove(weaponorig, weapondata, numobj[WEAPON][version]*WEAPON_FIELDS*4);
memmove(textorigp, textdatap, size[TXT][version]);
Loaddoom(doombakfp);
// Check each element in each array of the original data vs the
// info found in the backup exe. If they're different, copy the old
// value to the patch file.
for (i=0; i<numobj[THING][version]-1; i++)
{
nameprinted = NO;
for (j=0; j<THING_FIELDS; j++)
{
if (thingorig[i][j] != thingdata[i][j])
{
if (nameprinted == NO)
{
fprintf(patchp, "\nThing %d (%s)\n", i+1, namelist[i]);
nameprinted = YES;
}
fprintf(patchp, "%s = %ld\n", thingfields[j], thingorig[i][j]);
}
}
}
for (i=0; i<numobj[SOUND][version]; i++)
{
nameprinted = NO;
for (j=0; j<SOUND_FIELDS; j++)
{
if (soundorig[i][j] != sounddata[i][j])
{
if (nameprinted == NO)
{
fprintf(patchp, "\nSound %d\n", i+1);
nameprinted = YES;
}
fprintf(patchp, "%s = %ld\n", soundfields[j], soundorig[i][j]);
}
}
}
for (i=0; i<numobj[FRAME][version]; i++)
{
nameprinted = NO;
for (j=0; j<FRAME_FIELDS; j++)
{
// I may draw flak for this one, but don't save the action
// pointers to the .deh file. They're different every version,
// but make no difference at all.
if ((frameorig[i][j] != framedata[i][j]) &&
(j != ACTIONPTR))
{
if (nameprinted == NO)
{
fprintf(patchp, "\nFrame %d\n", i);
nameprinted = YES;
}
fprintf(patchp, "%s = %ld\n", framefields[j], frameorig[i][j]);
}
}
}
for (i=0; i<numobj[SPRITE][version]; i++)
if (spriteorig[i] != spritedata[i])
fprintf(patchp, "\nSprite %d\nOffset = %ld\n", i, spriteorig[i]);
for (i=0; i<numobj[AMMO][version]; i++)
{
nameprinted = NO;
if (maxammoorig[i] != maxammodata[i])
{
fprintf(patchp, "\nAmmo %d (%s)\n", i, ammolist[i]);
nameprinted = YES;
fprintf(patchp, "Max ammo = %ld\n", maxammoorig[i]);
}
if (perammoorig[i] != perammodata[i])
{
if (nameprinted == NO)
fprintf(patchp, "\nAmmo %d (%s)\n", i, ammolist[i]);
fprintf(patchp, "Per ammo = %ld\n", perammoorig[i]);
}
}
for (i=0; i<numobj[WEAPON][version]; i++)
{
nameprinted = NO;
for (j=0; j<WEAPON_FIELDS; j++)
{
if (weaponorig[i][j] != weapondata[i][j])
{
if (nameprinted == NO)
{
fprintf(patchp, "\nWeapon %d (%s)\n", i, weaponlist[i]);
nameprinted = YES;
}
fprintf(patchp, "%s = %ld\n", weaponfields[j], weaponorig[i][j]);
}
}
}
// Write the text LAST, since it's the most error-prone part, and
// hardest to recover from. And skip the last few chars, since some-
// thing wierd happens if I include them. Hmmm..
if ( Lnx_DOOM ) {
lnx_savetxt(textorigp, textdatap, size[TXT][version], patchp);
} else {
for (i=0; i<size[TXT][version]-4; i += (strlen(textorigp+i) & ~3) + 4) {
if (strcmp(textorigp+i, textdatap+i) != 0) {
fprintf(patchp, "\nText %d %d\n%s%s\n",
strlen(textdatap+i), strlen(textorigp+i), textdatap+i, textorigp+i);
}
}
}
// Move all of the data back to its original location.
memmove(thingdata, thingorig, numobj[THING][version]*THING_FIELDS*4);
memmove(sounddata, soundorig, numobj[SOUND][version]*SOUND_FIELDS*4);
memmove(framedata, frameorig, numobj[FRAME][version]*FRAME_FIELDS*4);
memmove(spritedata, spriteorig, numobj[SPRITE][version]*4);
memmove(maxammodata, maxammoorig, numobj[AMMO][version]*4);
memmove(perammodata, perammoorig, numobj[AMMO][version]*4);
memmove(weapondata, weaponorig, numobj[WEAPON][version]*WEAPON_FIELDS*4);
memmove(textdatap, textorigp, size[TXT][version]);
// Free up the memory
delete thingorig;
delete soundorig;
delete frameorig;
delete spriteorig;
delete maxammoorig;
delete perammoorig;
delete weaponorig;
delete textorigp;
return 0;
}
// Creates the back-up file that gets hacked, copied straight from the
// original Doom file.
void CreateDoomhack(void)
{
char *buffer;
long cursize = 0;
long totalsize;
// Get some space in memory to use as a copy place.
if ((buffer = new char[4096]) == NULL)
AbortProg("in GetDoomFiles!");
// Open the new file, find the size of the original exe file.
printf("\r\nCreating %s...\r\n", doomexe);
doomexefp = fopen(doomexe, "wb");
fseek(doombakfp, 0, SEEK_END);
totalsize = ftell(doombakfp);
fseek(doombakfp, 0, SEEK_SET);
// Continue copying chunks, as long as there are chunks to be
// copied.
while (cursize < totalsize - 4096)
{
fread(buffer, 4096, 1, doombakfp);
fwrite(buffer, 4096, 1, doomexefp);
cursize += 4096;
}
fread(buffer, (unsigned int)(totalsize-cursize), 1, doombakfp);
fwrite(buffer, (unsigned int)(totalsize-cursize), 1, doomexefp);
fclose(doomexefp);
delete[] buffer;
}
// Finds the doom(2).exe and doom(2).wad files, sets a pointer
// to the doom.exe file.
int GetDoomFiles(char *arg1)
{
char wadstring[5];
char keypress;
ResourceS entry = {0, 0, ""};
// Check for the WAD and original exe.
if ((Checkforfile(doombak, arg1, ".exe", "rb", &doombakfp) == -1) ||
(Checkforfile(doomwad, arg1, ".wad", "rb", &doomwadfp) == -1))
{
printf("\nCannot find a necessary Doom file, aborting!\n\n");
Printoptions();
return -1;
}
// Check for the hack exe. If it's not there, ask the user if he
// wants to create it.
if (Checkforfile(doomexe, arg1, "hack.exe", "r+b", &doomexefp) == -1)
{
puts("\nCannot find the backup exe file to edit. DeHackEd will create");
puts("a copy of your Doom exe file and edit the copy rather than the");
printf("main exe file itself. Do you want to do this? ");
keypress = getch();
if (tolower(keypress) == 'y')
{
CreateDoomhack();
if (Checkforfile(doomexe, arg1, "hack.exe", "r+b", &doomexefp) == -1)
{
puts("\nCreation failed!");
return -1;
}
}
else
return -1;
}
// Compare file sizes. Who knows what fun things would happen if they
// were different versions?? Yuck!
fseek(doomexefp, 0, SEEK_END);
fseek(doombakfp, 0, SEEK_END);
if (ftell(doomexefp) != ftell(doombakfp))
{
printf("\nThe hacking exe file (%s) and regular exe file\n", doomexe);
printf("(%s) are not from the same version of Doom. Would\n", doombak);
puts("you like to erase the old hacking exe file and create a new one");
printf("for your current Doom version? ");
keypress = getch();
if (tolower(keypress) == 'y')
{
CreateDoomhack();
if (Checkforfile(doomexe, arg1, "hack.exe", "r+b", &doomexefp) == -1)
{
puts("\nCreation failed!");
return -1;
}
}
else
return -1;
}
// Do doom.wad checking...
fseek(doomwadfp, 0, SEEK_SET);
fread(wadstring, 4, 1, doomwadfp);
wadstring[4] = 0;
fseek(doomwadfp, 0, SEEK_END);
// Check WAD size, the first 4 bytes, and a random entry in the WAD.
// You are under strict orders not to change this check.
if (ftell(doomwadfp) < 8000000L || (strcmp(wadstring, "IWAD") != 0)
|| (Searchforentry("BFS1A0", &entry) == 0))
{
puts("A registered Doom WAD file has not been detected.");
puts("If you have not registered Doom, you must do so before");
puts("you can use this program. If you have registered Doom");
puts("and are still getting this error, please contact Greg Lewis");
puts("at gregl@umich.edu about this problem.");
return -1;
}
return 0;
}
// Gets the next line of the patch file that's being loaded
#define whitespace(c) ((c == ' ')||(c == '\t')||(c == '\r')||(c == '\n'))
int GetNextLine(char *nextline, int *numlines, FILE *patchp)
{
char buffer[512], *line;
int i;
while ( fgets(buffer, 511, patchp) ) {
++(*numlines);
for ( i=strlen(buffer); i && whitespace(buffer[i-1]); --i );
buffer[i]='\0';
for ( line=buffer; *line && whitespace(*line); ++line );
if ( !*line || (*line == '#') )
continue;
strcpy(nextline, line);
return(1);
}
return(0);
}
// Loads the new text file format. Similar to CreateDiffSave...
int LoadDiff(FILE *patchp)
{
int tempversion, patchformat;
int error = 0;
char *nextline, *line2;
int result;
int curType = 0;
int curNumber;
int numlines = 1;
EBool matched, valid, lnx_ok, AbortLoop = NO;
int i;
int length1, length2;
char *errormsg[3] = {"Line %d: No value after equal sign.",
"Line %d: No value before equal sign.",
"Line %d: Invalid single word line."};
// Get memory for a line.
if ((nextline = new char[120]) == NULL)
AbortProg("in LoadDiff");
// See Savepatch for file formats
// Find an end-of-line somewere after the version number, which
// should have been read in the main LoadPatch routine.
while (fgetc(patchp) != '\n')
;
// Process the whole file one line at a time. GetNextLine returns 0
// at EOF.
while (GetNextLine(nextline, &numlines, patchp) && !AbortLoop)
{
// Set matched to NO to be sure we catch any errors. Also assume it's
// valid unless flagged otherwise.
matched = NO;
valid = YES;
// Parse the line the for spaces or equal signs.
result = ProcessLine(nextline, &line2);
// Result determines what happened during parsing... whether we
// have an error (negative) or an equals sign (1) or a space after
// a word (2).
switch (result)
{
case 1:
// Found an equals sign. Check for all possible lvalues
// and take the appropriate action.
if (strcmpi(nextline, "doom version") == 0)
{
matched = YES;
if ((sscanf(line2, "%d", &tempversion) == 0) ||
((tempversion != 12) && (tempversion != 16) &&
(tempversion != 17) && (tempversion != 18) &&
(tempversion != 19) && (tempversion != 20)))
{
sprintf(nextline, "Line %d: Invalid Doom version number, assuming 1.9.", numlines);
AbortLoop = Printwindow(nextline, ERROR);
error = 2;
tempversion = 19;
}
}
else if (strcmpi(nextline, "patch format") == 0)
{
matched = YES;
if ((sscanf(line2, "%d", &patchformat) == 0) ||
(patchformat != 5))
{
sprintf(nextline, "Line %d: Invalid patch format number, assuming format 5.", numlines);
AbortLoop = Printwindow(nextline, ERROR);
error = 2;
patchformat = 5;
}
}
// For any particular mode that we're in there could be multiple
// lvalues, stored in the "...fields" arrays. Check them all.
else if (curType == THING)
{
for (i=0; i<THING_FIELDS; i++)
if ((strcmpi(nextline, thingfields[i]) == 0) &&
!(version == DOOM1_12 && i == RESPAWNFRAME))
{
matched = YES;
if (sscanf(line2, "%ld", &(thingdata[curNumber-1][i])) == 0)
valid = NO;
}
}
else if (curType == FRAME)
{
for (i=0; i<FRAME_FIELDS; i++)
if (strcmpi(nextline, framefields[i]) == 0)
{
matched = YES;
if (sscanf(line2, "%ld", &(framedata[curNumber][i])) == 0)
valid = NO;
}
}
else if (curType == SOUND)
{
for (i=0; i<SOUND_FIELDS; i++)
if (strcmpi(nextline, soundfields[i]) == 0)
{
matched = YES;
if (sscanf(line2, "%ld", &(sounddata[curNumber][i])) == 0)
valid = NO;
}
}
else if (curType == SPRITE)
{
if (strcmpi(nextline, "offset") == 0)
{
matched = YES;
if (sscanf(line2, "%ld", &(spritedata[curNumber])) == 0)
valid = NO;
}
}
else if (curType == AMMO)
{
if (strcmpi(nextline, "Max ammo") == 0)
{
matched = YES;
if (sscanf(line2, "%ld", &(maxammodata[curNumber])) == 0)
valid = NO;
}
else if (strcmpi(nextline, "Per ammo") == 0)
{
matched = YES;
if (sscanf(line2, "%ld", &(perammodata[curNumber])) == 0)
valid = NO;
}
}
else if (curType == WEAPON)
{
for (i=0; i<WEAPON_FIELDS; i++)
if (strcmpi(nextline, weaponfields[i]) == 0)
{
matched = YES;
if (sscanf(line2, "%ld", &(weapondata[curNumber][i])) == 0)
valid = NO;
}
}
break;
case 2:
// Found two words (or more) on a line. Check for all
// appropriate meanings and take the appropriate action.
// These should be the section headers "Thing 1 (Player)".
// Compare the first word with all known object types
// ("Thing", "Frame", etc...)
matched = NO;
for (i=0; i < NUMDATA; i++)
if (strcmpi(nextline, datanames[i]) == 0)
{
matched = YES;
curType = i;
if (sscanf(line2, "%d", &curNumber) == 0)
{
sprintf(nextline, "Line %d: Unreadable %s number.", numlines, datanames[i]);
AbortLoop = Printwindow(nextline, ERROR);
error = 2;
}
else if ((curNumber < 0) ||
((curType == TXT) && (curNumber > size[curType][version])) ||
((curType != TXT) && (curNumber > numobj[curType][version])))
{
sprintf(nextline, "Line %d: %s number out of range.", numlines, datanames[i]);
AbortLoop = Printwindow(nextline, ERROR);
error = 2;
}
}
// This is a special case... gotta read the text directly
// from the next line if we found a "Text" identifier.
if (curType == TXT)
{
if (sscanf(line2, "%d %d", &length1, &length2) < 2)
{
sprintf(nextline, "Line %d: Unreadable length value, aborting read.", numlines);
error = 1;
goto ErrorJump;
}
else
{
line2 = new char[length1+1];
// Just so ya know, I really hate doing this. Anyways,
// read in whole string 1 char at a time, checking for
// 0D 0A combos, which are really new-lines.
for (i=0; i<length1; i++)
{
fread(line2+i, 1, 1, patchp);
if (line2[i] == 0x0D)
{
fread(line2+i, 1, 1, patchp);
line2[i] = '\n';
numlines++;
} else if (line2[i] == 0x0A)
++numlines;
}
line2[length1] = 0;
// OK, skip through the enter text section, trying to match
// up the string.
valid = NO;
if ( Lnx_DOOM ) {
valid = lnx_matchtxt(textdatap, size[TXT][version], line2, &curNumber);
} else {
for (i=0; i<size[TXT][version]-4; i += (strlen(textdatap+i) & ~3) + 4) {
if (strcmp(textdatap+i, line2) == 0)
{
curNumber = i;
valid = YES;
break;
}
}
}
lnx_ok = YES;
if ( Lnx_DOOM ) {
// String 2 must be shorter or equal to String 1.
if ( length2 > length1 )
lnx_ok = NO;
}
if ( (valid == YES) && lnx_ok ) {
// Just so ya know, I really hate doing this. Anyways,
// read in whole string 1 char at a time, checking for
// 0D 0A combos, which are really new-lines.
for (i=curNumber; i<curNumber+length2; i++)
{
fread(textdatap+i, 1, 1, patchp);
if (textdatap[i] == 0x0D)
{
fread(textdatap+i, 1, 1, patchp);
textdatap[i] = '\n';
numlines++;
} else if (textdatap[i] == 0x0A)
++numlines;
}
if ( Lnx_DOOM ) {
while ( length2 < length1 )
*(textdatap+(curNumber+length2++)) = ' ';
}
textdatap[curNumber+length2] = 0;
}
else
{
// Eat the rest of the patch...
for ( i=0; i<length2; ++i ) {
fread(nextline, 1, 1, patchp);
if (nextline[0] == 0x0D) {
fread(nextline, 1, 1, patchp);
numlines++;
} else if (nextline[0] == 0x0A)
++numlines;
}
sprintf(nextline, "Line %d: Unmatchable text string.", numlines);
AbortLoop = Printwindow(nextline, ERROR);
error = 2;
valid = YES;
}
delete line2;
}
// Reset the current type
curType = 0;
}
break;
case -1:
case -2:
case -3:
// Error. Print error, and quit loading patch file entirely if
// the user pressed Escape at the Printwindow prompt.
sprintf(nextline, errormsg[-result-1], numlines);
AbortLoop = Printwindow(nextline, ERROR);
error = 2;
break;
}
// Whoops, it didn't match anything at all...
if ((matched == NO) && (result >= 0))
{
sprintf(nextline, "Line %d: Unknown line.", numlines);
AbortLoop = Printwindow(nextline, ERROR);
error = 2;
}
// Whoops, invalid number somewhere...
if (valid == NO)
{
sprintf(nextline, "Line %d: Unreadable value in a %s field.", numlines, datanames[curType]);
AbortLoop = Printwindow(nextline, ERROR);
error = 2;
}
}
// Jump straight here if we have an unrecoverable error. Yeah, yeah, I
// know, but gotos are the easiest way.
ErrorJump:
// Print different messages according to results.
if (AbortLoop)
{
Printwindow("Patch file load aborted by user.", ERROR);
delete nextline;
return -1;
} else if (error == 0) {
delete nextline;
return 0;
} else if (error == 1) {
Printwindow(nextline, ERROR);
delete nextline;
return -1;
} else {
Printwindow("Patch file read, one or more errors detected.", ERROR);
delete nextline;
return -1;
}
}
// Loads all of the data from the exe into the correct data structures.
void Loaddoom(FILE *exefp)
{
int i;
// Read Thing data
fseek(exefp, offset[THING][version], SEEK_SET);
for (i=0; i<numobj[THING][version]-1; i++) {
fread((void *)thingdata[i], size[THING][version], 1, exefp);
bytesex_row(thingdata[i], THING_FIELDS, version);
}
// Read Sound data
fseek(exefp, offset[SOUND][version], SEEK_SET);
fread((void *)sounddata, size[SOUND][version], numobj[SOUND][version], exefp);
bytesex_table(sounddata, numobj[SOUND][version], SOUND_FIELDS, version);
// Read Frame data
fseek(exefp, offset[FRAME][version], SEEK_SET);
fread((void *)framedata, size[FRAME][version], numobj[FRAME][version], exefp);
bytesex_table(framedata, numobj[FRAME][version], FRAME_FIELDS, version);
// Read Sprite data
fseek(exefp, offset[SPRITE][version], SEEK_SET);
fread((void *)spritedata, size[SPRITE][version], numobj[SPRITE][version], exefp);
bytesex_row(spritedata, numobj[SPRITE][version], version);
// Read Ammo data
fseek(exefp, offset[AMMO][version], SEEK_SET);
fread((void *)maxammodata, size[AMMO][version], numobj[AMMO][version], exefp);
bytesex_row(maxammodata, numobj[AMMO][version], version);
fread((void *)perammodata, size[AMMO][version], numobj[AMMO][version], exefp);
bytesex_row(perammodata, numobj[AMMO][version], version);
// Read Weapon data
fseek(exefp, offset[WEAPON][version], SEEK_SET);
fread((void *)weapondata, size[WEAPON][version], numobj[WEAPON][version], exefp);
bytesex_table(weapondata, numobj[WEAPON][version], WEAPON_FIELDS, version);
// Read Text data
if ( Lnx_DOOM ) {
lnx_loadtxt(textdatap, &size[TXT][version],
&numobj[TXT][version], exefp);
} else {
fseek(exefp, offset[TXT][version], SEEK_SET);
fread((void *)textdatap, size[TXT][version], 1, exefp);
}
}
// Loads a patch file, the old format.
// We don't support bytesex on the old-format patches. Is it worth the trouble?
int LoadOld(FILE *patchp)
{
char buffer[80];
char tempversion, patchformat;
EBool error = YES;
int i, truepatch, offset;
EBool foundend = NO;
fread(&tempversion, sizeof(char), 1, patchp);
fread(&patchformat, sizeof(char), 1, patchp);
if (patchformat == 3)
strcpy(buffer, "Doom 1.6 beta patches are no longer valid. Sorry!");
else if (patchformat != 4)
strcpy(buffer, "Bad patch version number!!");
else if (tempversion != 12 && version == DOOM1_2)
strcpy(buffer, "This patch requires a higher Doom version!");
else if (tempversion > 20 || tempversion < 16)
strcpy(buffer, "Bad Doom release number found!");
else
error = NO;
if (error == YES)
{
Printwindow(buffer, ERROR);
return -1;
}
// OK, if it passes all the tests, load the sucker in.
fread(thingdata, size[THING][version], numobj[THING][version]-1, patchp);
fread(maxammodata, size[AMMO][version], numobj[AMMO][version], patchp);
fread(perammodata, size[AMMO][version], numobj[AMMO][version], patchp);
fread(weapondata, size[WEAPON][version], numobj[WEAPON][version], patchp);
fread(framedata, size[FRAME][version], numobj[FRAME][version], patchp);
// These sections ARE different between Doom 2 and Doom 1.666...
// only load them in straight if they are the correct version.
if ((tempversion == 20 && truever == DOOM2_16) ||
(tempversion == 16 && truever == DOOM1_16) ||
(tempversion == 17 && truever == DOOM2_17) ||
(tempversion == 18 && truever == DOOM2_17A)||
(tempversion == 19 && truever == DOOM2_19))
{
fread(sounddata, size[SOUND][version], numobj[SOUND][version], patchp);
fread(spritedata, size[SPRITE][version], numobj[SPRITE][version], patchp);
fread(textdatap, size[TXT][version], 1, patchp);
}
else if ((tempversion == 16 && (truever == DOOM2_17 || truever == DOOM2_17A)) ||
(tempversion == 17 && (truever == DOOM1_16 || truever == DOOM2_17A)) ||
(tempversion == 18 && (truever == DOOM1_16 || truever == DOOM2_17)))
{
// Otherwise try to convert them as best we can.
fread(sounddata, size[SOUND][version], numobj[SOUND][version], patchp);
fread(spritedata, size[SPRITE][version], numobj[SPRITE][version], patchp);
fread(textdatap, size[TXT][version], 1, patchp);
// Finds the text differences between the version it's loading
// and the version it's using. Adds that to each sound/sprite.
switch (tempversion)
{
case 16: truepatch = DOOM1_16; break;
case 17: truepatch = DOOM2_17; break;
case 18: truepatch = DOOM2_17A; break;
}
offset = diffoffset[truever][2] - diffoffset[truepatch][2];
for (i=0; i<numobj[SOUND][version]; i++)
sounddata[i][TEXTP] += offset;
for (i=0; i<numobj[SPRITE][version]; i++)
spritedata[i] += offset;
// The following is ugly. Depending on what version we're
// loading and what version we're in, tweak the text data
// to "fit" the new version.
if (diffoffset[truever][2] < diffoffset[truepatch][2])
{
memmove(&(textdatap[diffoffset[truever][0]]),
&(textdatap[diffoffset[truepatch][0]]),
diffoffset[truever][1] - diffoffset[truever][0]);
memmove(&(textdatap[diffoffset[truever][1]]),
&(textdatap[diffoffset[truepatch][1]]),
diffoffset[truever][2] - diffoffset[truever][1]);
memmove(&(textdatap[diffoffset[truever][2]]),
&(textdatap[diffoffset[truepatch][2]]),
size[TXT][version]-diffoffset[truepatch][2]);
}
else
{
memmove(&(textdatap[diffoffset[truever][2]]),
&(textdatap[diffoffset[truepatch][2]]),
size[TXT][version]-diffoffset[truepatch][2]-offset);
memmove(&(textdatap[diffoffset[truever][1]]),
&(textdatap[diffoffset[truepatch][1]]),
diffoffset[truever][2] - diffoffset[truever][1]);
memmove(&(textdatap[diffoffset[truever][0]]),
&(textdatap[diffoffset[truepatch][0]]),
diffoffset[truever][1] - diffoffset[truever][0]);
}
// These two chunks make sure that the "title" strings are OK
// after the above tweaking.
for (i=diffoffset[truever][0]; i<diffoffset[truever][1]-2; i++)
if (textdatap[i] == 0 || foundend == YES)
{
textdatap[i] = ' ';
foundend = YES;
}
textdatap[i] = 0;
foundend = NO;
for (i=diffoffset[truever][1]; i<diffoffset[truever][2]-2; i++)
if (textdatap[i] == 0 || foundend == YES)
{
textdatap[i] = ' ';
foundend = YES;
}
textdatap[i] = 0;
}
else
Printwindow("Patch made with different version exe. Loading compatible data.", INFO);
return 0;
}
// Loads a patch file
int Loadpatch(char *filename)
{
FILE *patchp;
char tempversion, patchformat;
char fullname[150] = "";
EBool error = NO;
char idstring[30];
int i;
// Fix the patch filename, put on the patchdir if we need to.
if ( (filename[0] != '/') && (filename[0] != '.') ) {
strcpy(fullname, patchdir);
if (fullname[0] != 0)
strcat(fullname, "/");
}
strcat(fullname, filename);
// Tack on the extension if there isn't one.
Preparefilename(fullname);
// Try to open the file, return an error if we can't for some reason.
if ((patchp = fopen(fullname, "rb")) == NULL)
if (errno == 2)
{
sprintf(filename, "File %s does not exist!", fullname);
Printwindow(filename, ERROR);
return -1;
}
else
{
sprintf(filename, "Error reading %s.", fullname);
Printwindow(filename, ERROR);
return -1;
}
// See Savepatch for file formats
fseek(patchp, 0, SEEK_SET);
// Read in the first character, and compare what it is to see if
// we are dealing with one of the really old patch formats.
fread(&tempversion, sizeof(char), 1, patchp);
// Tempversion of 12 is Doom 1.2
if (tempversion == 12)
{
// The only patch formats for 1.2 are 1 and 2.
fread(&patchformat, sizeof(char), 1, patchp);
if (patchformat != 1 && patchformat != 2)
{
Printwindow("Unknown patch file format!", ERROR);
error = YES;
goto ErrorJump;
}
// If the current version isn't Doom 1.2, we'll need to convert
// the patch.
if (version != DOOM1_2)
{
Convertpatch(patchp, patchformat);
Printwindow("Patch converted from DHE 1.3 format.", INFO);
}
else
{
// Read in the Doom 1.2 format.
for (i=0; i<numobj[THING][version]-1; i++)
fread(thingdata[i], size[THING][DOOM1_2], 1, patchp);
fread(maxammodata, size[AMMO][DOOM1_2], numobj[AMMO][DOOM1_2], patchp);
fread(perammodata, size[AMMO][DOOM1_2], numobj[AMMO][DOOM1_2], patchp);
fread(weapondata, size[WEAPON][DOOM1_2], numobj[WEAPON][DOOM1_2], patchp);
if (patchformat == 2)
fread(framedata, size[FRAME][DOOM1_2], numobj[FRAME][DOOM1_2], patchp);
}
}
else if (tempversion == 'P')
{
// OK, so it's one of the newer versions.
fread(idstring, 24, 1, patchp);
idstring[24] = 0;
if (stricmp(idstring, "atch File for DeHackEd v") != 0)
{
Printwindow("This is not a DeHackEd patch file!", ERROR);
error = YES;
goto ErrorJump;
}
// Read in the version number, and convert it to an int to check
// if we have a bad version.
fread(idstring, 3, 1, patchp);
idstring[3] = 0;
idstring[4] = (idstring[0]-'0')*10+(idstring[2]-'0');
if (idstring[4] > 23)
{
Printwindow("This patch file requires a newer DeHackEd release!", ERROR);
error = YES;
}
else if (idstring[4] < 20)
{
Printwindow("This patch file has an incorrect version number!", ERROR);
error = YES;
}
else if (idstring[4] == 23)
{
if (LoadDiff(patchp) == -1)
error = YES;
}
else
{
if (LoadOld(patchp) == -1)
error = YES;
}
}
else
{
// The catch-all
Printwindow("This is not a DeHackEd patch file!", ERROR);
error = YES;
}
ErrorJump:
fclose(patchp);
if (error == YES)
return -1;
sprintf(filename, "Patch file %s read.", fullname);
Printwindow(filename, INFO);
return 0;
}
// OldSave, saves in the old DeHackEd formats.
// We don't support bytesex on the old-format patches. Is it worth the trouble?
// Oldest patch file format:
// (char) Doom.exe version # (12, 16, etc.)
// (char) Patch file format # (1 for DeHackEd 1.2 patch files, 2 for
// DeHackEd 1.3 patch files)
// The data. Consists of the Thing data, maxAmmo data, perAmmo data,
// Weapon data, and if it's patch version 2, Frame data.
// Old patch file format:
// (char)*28 "Patch File for DeHackEd v?.?" Header, and DHE version #
// (char) Version of Doom, 0 for 1.2, 1 for 1.666, 2 for 2.0
// (char) Patch file format, 1, 2 and 3 are Old patches files,
// 4 is the version for this one
// Data structures, written directly. In this order:
// thing, maxammo, perammo, weapon, frame, sound, sprite, text
int OldSave(char *filename, EBool Overwrite)
{
FILE *patchp;
char tempver;
char format;
char fullname[150] = "";
int i;
// Find out what version to call it.
switch (truever)
{
case DOOM1_12:
tempver = 12;
break;
case DOOM1_16:
tempver = 16;
break;
case DOOM2_16:
tempver = 20;
break;
case DOOM2_17:
tempver = 17;
break;
case DOOMX_18: // Linux Doom uses DOOM2_19 patches
case DOOMS_18:
case DOOM_SGI:
case DOOM2_17A:
tempver = 18; // Yeah, this is a kludge
case DOOM2_18:
break; // Not supported.
case DOOM2_19:
tempver = 19;
break;
case USERDEF:
tempver = 19; // What do we do with USERDEF? FIXME!!
break;
}
// Try to switch to the patch directory
i = chdir(patchdir);
if (i == -1 && patchdir[0] != 0)
{
sprintf(filename, "Patch directory %s not found!", patchdir);
return ERROR;
}
chdir(curdir);
// Prepend the patch directory if necessary.
if ( (filename[0] != '/') && (filename[0] != '.') ) {
strcpy(fullname, patchdir);
if (fullname[0] != 0)
strcat(fullname, "/");
}
strcat(fullname, filename);
// Turn it into a valid filename, add the extension if necessary.
Preparefilename(fullname);
// If we open it and it's not NULL, it already exists. Return an
// error in this case, or just continue if the Overwrite variable is
// set.
if ((patchp = fopen(fullname, "rb")) != NULL)
{
if (Overwrite == NO)
{
fclose(patchp);
return -1;
}
}
else if (errno != 2)
{
sprintf(filename, "Error writing %s!", fullname);
return ERROR;
}
// Close the file for read-only and reopen it for writing.
fclose(patchp);
patchp = fopen(fullname, "wb");
if (version == DOOM1_2)
format = 2;
else
{
// This must continue to be "v2.0" even though we're on a higher
// version, because my compatibility scheme backfired. DHE v2.0
// checks for this string, so it's gotta be there. Urg.
format = 4;
fwrite("Patch File for DeHackEd v2.0", 28, 1, patchp);
}
fwrite(&tempver, sizeof(char), 1, patchp);
fwrite(&format, sizeof(char), 1, patchp);
for (i=0; i<numobj[THING][version]-1; i++)
fwrite(thingdata[i], size[THING][version], 1, patchp);
fwrite(maxammodata, size[AMMO][version], numobj[AMMO][version], patchp);
fwrite(perammodata, size[AMMO][version], numobj[AMMO][version], patchp);
fwrite(weapondata, size[WEAPON][version], numobj[WEAPON][version], patchp);
fwrite(framedata, size[FRAME][version], numobj[FRAME][version], patchp);
if (version != DOOM1_2)
{
fwrite(sounddata, size[SOUND][version], numobj[SOUND][version], patchp);
fwrite(spritedata, size[SPRITE][version], numobj[SPRITE][version], patchp);
fwrite(textdatap, size[TXT][version], 1, patchp);
}
fclose(patchp);
sprintf(filename, "Patch file %s written.", fullname);
return INFO;
}
// Parses the config file
void Parseconfigfile(void)
{
FILE *cfgfp;
char nextline[256];
char *line2;
int i;
int numlines = 1;
EBool match = NO;
int tempver, result;
char *options[19] = {"pathname",
"editname",
"normalname",
"wadname",
"params",
"patchdir",
"version",
"size",
"thingoff",
"soundoff",
"frameoff",
"spriteoff",
"ammooff",
"weaponoff",
"textoff",
"codepoff",
"sbaddress",
"sbirq",
"sbdma"};
char *strptrs[6] = {doompath, doomexe, doombak, doomwad, doomargs,
patchdir};
if ((cfgfp = fopen("dehacked.ini", "rt")) == NULL)
{
puts("dehacked.ini not found.");
return;
}
while (GetNextLine(nextline, &numlines, cfgfp))
{
// Parse the line the for spaces or equal signs.
result = ProcessLine(nextline, &line2);
switch (result)
{
case 1:
for (i=0; i<19; i++)
{
if (strcmpi(nextline, options[i]) == 0)
{
match = YES;
switch (i)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
strcpy(strptrs[i], line2);
break;
case 6:
sscanf(line2, "%d", &tempver);
if (tempver == 0)
version = DOOM1_2;
else if (tempver == 1)
version = DOOM1_6;
else if (tempver == 2)
version = DOOM2_0;
else if (tempver == 3)
version = DOOM1_9;
break;
case 7:
sscanf(line2, "%ld", &doomsize);
break;
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
sscanf(line2, "%ld", &(offset[i-8]));
break;
}
}
}
break;
case -1: printf("Line %d: No value after equal sign.", numlines);
break;
case -2: printf("Line %d: No value before equal sign.", numlines);
break;
case 2:
case -3: printf("Line %d: Invalid single-word line detected.", numlines);
break;
}
if (match == NO)
{
printf("Line %d: Cannot match variable \"%s\" in dehacked.ini!", numlines, nextline);
break;
}
else
match = NO;
}
fclose(cfgfp);
}
// Finds the filename in a path\filenames combination and turns it into
// familiar <8>.<3> combination if it's invalid. Also appends ".deh"
// if there is no extension.
void Preparefilename(char *fullname)
{
int i = 0;
char *filename, *dot = NULL;
filename = fullname;
// Filename is a pointer to the actual filename, without the path. So
// step through the full path/filename combo, and keep moving the
// current location of filename whenever we come to a directory separator.
while (fullname[i] != 0)
{
if (fullname[i] == '/')
filename = fullname+i+1;
i++;
}
// Try to find the extension of the filename
for (i=0; i<strlen(filename); i++)
if (filename[i] == '.')
{
dot = filename + i;
break;
}
if (dot == NULL)
{
// OK, no extension at all, so add our own at the correct place.
if (strlen(filename) > 8)
filename[8] = 0;
strcat(filename, ".deh");
}
else
{
if (dot - filename > 8)
{
strncpy(filename + 8, dot, 4);
filename[12] = 0;
}
else
dot[4] = 0;
}
}
// This procedure processes a line from a patch file. If an equals sign
// exists in the input line, the following is done:
// *next line
// \---> first part = second part
// zero byte --^ ^--- *line2
// If there is no equals sign, the following is done:
// *next line
// \---> word1 word2 and other words
// zero byte --^^--- *line2
// Return values:
// 1 Successful - found an equals sign
// 2 Successful - found a word
// -1 No info after equals sign
// -2 No value before equals sign
// -3 No info after first word in line
int ProcessLine(char *nextline, char **line2)
{
int i = 0, j = 0;
// Search line for an =
while (nextline[i] != 0 && nextline[i] != '=')
i++;
// If we found one...
if (nextline[i] == '=')
{
// Search for the first non-space after the =.
j = i--;
while (isspace(nextline[++j]))
;
// It was all whitespace, error... should be equal to something
if (nextline[j] == 0)
return -1;
// Set line2 to the first non-space after an =
*line2 = nextline+j;
// Kill any whitespace before the =...
while (i >= 0 && isspace(nextline[i]))
i--;
// It was all whitespace, error... should be something before =
if (i == -1)
return -2;
// OK, put in an end-of-string character to kill the space(s)
nextline[i+1] = 0;
// Successful
return 1;
}
// Otherwise, the line should have two separate words on it
else
{
// Search for first space on the line
while (nextline[j] != 0 && !isspace(nextline[j]))
j++;
// Only one word on line, didn't find any spaces at all
if (nextline[j] == 0)
return -3;
// Found some space(s), now search for the second word
i = j;
while (isspace(nextline[++i]))
;
// No non-spaces after the first word
if (nextline[i] == 0)
return -3;
// Set this to the first letter of the second word
*line2 = nextline+i;
// Terminate the first word's string
nextline[j] = 0;
// Successful
return 2;
}
}
// Saves a patch file.
// Current patch file format:
// Patch file format:
// "Patch File for DeHackEd v?.?" Header, and DeHackEd version #
// "Doom version = ??" Version of Doom
// "Patch format = ?" Patch file format, 5 is the current one
//
// Data structures, stored as text, in this order:
// thing, sound, frame, sprite, ammo, weapon, text
int Savepatch(char *filename, EBool Overwrite)
{
FILE *patchp;
char tempver;
char fullname[150] = "";
int i;
// Find out what version to call it.
switch (truever)
{
case DOOM1_12:
tempver = 12;
break;
case DOOM1_16:
tempver = 16;
break;
case DOOM2_16:
tempver = 20;
break;
case DOOM2_17:
tempver = 17;
break;
case DOOMX_18: // Linux Doom uses DOOM2_19 patches
case DOOMS_18:
case DOOM_SGI:
case DOOM2_17A:
tempver = 18; // Yeah, this is a kludge
case DOOM2_18:
break; // Not supported.
case DOOM2_19:
tempver = 19;
break;
case USERDEF:
tempver = 19; // What do we do with USERDEF? FIXME!!
break;
}
// Try to switch to the patch directory
i = chdir(patchdir);
if (i == -1 && patchdir[0] != 0)
{
sprintf(filename, "Patch directory %s not found!", patchdir);
return ERROR;
}
chdir(curdir);
// Prepend the patch directory if necessary.
if ( (filename[0] != '/') && (filename[0] != '.') ) {
strcpy(fullname, patchdir);
if (fullname[0] != 0)
strcat(fullname, "/");
}
strcat(fullname, filename);
// Turn it into a valid filename, add the extension if necessary.
Preparefilename(fullname);
// If we open it and it's not NULL, it already exists. Return an
// error in this case, or just continue if the Overwrite variable is
// set.
if ((patchp = fopen(fullname, "rt")) != NULL)
{
if (Overwrite == NO)
{
fclose(patchp);
return -1;
}
}
else if (errno != 2)
{
sprintf(filename, "Error writing %s!", fullname);
return ERROR;
}
// Close the file for read-only and reopen it for writing.
fclose(patchp);
patchp = fopen(fullname, "wt");
// This is the header for the patch files.
fprintf(patchp, "Patch File for DeHackEd v2.3\n\n");
fprintf(patchp, "# Note: Use the pound sign ('#') to start comment lines.\n\n");
fprintf(patchp, "Doom version = %d\n", tempver);
fprintf(patchp, "Patch format = 5\n\n");
// Do the bulk of the saving.
if (CreateDiffSave(patchp) == 0)
{
fclose(patchp);
sprintf(filename, "Patch file %s written.", fullname);
return INFO;
}
else
{
fclose(patchp);
AbortProg("in SavePatch");
return ERROR;
}
}
// This searches in the doom.wad file for an entry name.
// Returns 1 on success, 0 on failure.
int Searchforentry(char *name, ResourceS *entry)
{
unsigned long dirlength, dirstart;
int i;
// Read in the directory info (start of directory and number of
// entries).
fseek(doomwadfp, 4, SEEK_SET);
fread(&dirlength, 4, 1, doomwadfp);
fread(&dirstart, 4, 1, doomwadfp);
// Correct byte ordering... (wad is in little_endian form)
if ( NEED_SEX ) {
bytesex(dirlength);
bytesex(dirstart);
}
// Go to start of directory.
fseek(doomwadfp, dirstart, SEEK_SET);
// Scan through the directory, one by one, reading each in and
// checking if it matches the name we're seeking.
for (i=0; i<dirlength; i++)
{
fread(entry, 16, 1, doomwadfp);
if (strncmp(entry->resname, name, strlen(name)) == 0)
{
entry->resname[8] = 0;
if ( NEED_SEX ) {
bytesex(entry->resstart);
bytesex(entry->reslength);
}
return 1;
}
}
return 0;
}
// Write only the changeable data structures to the doom.exe
void Writedoom(void)
{
int i;
// Write the Thing stuff
fseek(doomexefp, offset[THING][version], SEEK_SET);
for (i=0; i<numobj[THING][version]-1; i++) {
bytesex_row(thingdata[i], THING_FIELDS, version);
fwrite(thingdata[i], size[THING][version], 1, doomexefp);
bytesex_row(thingdata[i], THING_FIELDS, version);
}
// Write Sound data
fseek(doomexefp, offset[SOUND][version], SEEK_SET);
bytesex_table(sounddata, numobj[SOUND][version], SOUND_FIELDS, version);
fwrite(sounddata, size[SOUND][version], numobj[SOUND][version], doomexefp);
bytesex_table(sounddata, numobj[SOUND][version], SOUND_FIELDS, version);
// Write Frame data
fseek(doomexefp, offset[FRAME][version], SEEK_SET);
bytesex_table(framedata, numobj[FRAME][version], FRAME_FIELDS, version);
fwrite(framedata, size[FRAME][version], numobj[FRAME][version], doomexefp);
bytesex_table(framedata, numobj[FRAME][version], FRAME_FIELDS, version);
// Write Sprite data
fseek(doomexefp, offset[SPRITE][version], SEEK_SET);
bytesex_row(spritedata, numobj[SPRITE][version], version);
fwrite(spritedata, size[SPRITE][version], numobj[SPRITE][version], doomexefp);
bytesex_row(spritedata, numobj[SPRITE][version], version);
// Write Ammo data
fseek(doomexefp, offset[AMMO][version], SEEK_SET);
bytesex_row(maxammodata, numobj[AMMO][version], version);
bytesex_row(perammodata, numobj[AMMO][version], version);
fwrite(maxammodata, size[AMMO][version], numobj[AMMO][version], doomexefp);
fwrite(perammodata, size[AMMO][version], numobj[AMMO][version], doomexefp);
bytesex_row(maxammodata, numobj[AMMO][version], version);
bytesex_row(perammodata, numobj[AMMO][version], version);
// Write Weapon data
fseek(doomexefp, offset[WEAPON][version], SEEK_SET);
bytesex_table(weapondata, numobj[WEAPON][version], WEAPON_FIELDS, version);
fwrite(weapondata, size[WEAPON][version], numobj[WEAPON][version], doomexefp);
bytesex_table(weapondata, numobj[WEAPON][version], WEAPON_FIELDS, version);
// Write Text data
if ( Lnx_DOOM ) {
lnx_writetxt(doomexefp);
} else {
fseek(doomexefp, offset[TXT][version], SEEK_SET);
fwrite(textdatap, size[TXT][version], 1, doomexefp);
}
// Make sure the file is executable. :)
fchmod(fileno(doomexefp), 0755);
}
// Write only the changeable data structures from the doom.exe
void Dumpdata(void)
{
FILE *output;
int i;
// Write the Thing stuff
if ( !(output=fopen("ThingData", "w")) ) {
Printwindow("Can't write to 'ThingData'", ERROR);
return;
}
for (i=0; i<numobj[THING][version]-1; i++)
fwrite(thingdata[i], size[THING][version], 1, output);
fclose(output);
// Write Sound data
if ( !(output=fopen("SoundData", "w")) ) {
Printwindow("Can't write to 'SoundData'", ERROR);
return;
}
fwrite(sounddata, size[SOUND][version], numobj[SOUND][version], output);
fclose(output);
// Write Frame data
if ( !(output=fopen("FrameData", "w")) ) {
Printwindow("Can't write to 'FrameData'", ERROR);
return;
}
fwrite(framedata, size[FRAME][version], numobj[FRAME][version], output);
fclose(output);
// Write Sprite data
if ( !(output=fopen("SpriteData", "w")) ) {
Printwindow("Can't write to 'SpriteData'", ERROR);
return;
}
fwrite(spritedata, size[SPRITE][version], numobj[SPRITE][version], output);
fclose(output);
// Write Ammo data
if ( !(output=fopen("AmmoData", "w")) ) {
Printwindow("Can't write to 'AmmoData'", ERROR);
return;
}
fwrite(maxammodata, size[AMMO][version], numobj[AMMO][version], output);
fwrite(perammodata, size[AMMO][version], numobj[AMMO][version], output);
fclose(output);
// Write Weapon data
if ( !(output=fopen("WeaponData", "w")) ) {
Printwindow("Can't write to 'WeaponData'", ERROR);
return;
}
fwrite(weapondata, size[WEAPON][version], numobj[WEAPON][version], output);
fclose(output);
// Write Text data
if ( !(output=fopen("TextData", "w")) ) {
Printwindow("Can't write to 'TextData'", ERROR);
return;
}
fwrite(textdatap, size[TXT][version], 1, output);
fclose(output);
}